package com.zhongpengcheng.blog.advice;

import com.zhongpengcheng.blog.exception.BaseResultCodeException;
import com.zhongpengcheng.blog.exception.CaptchaException;
import com.zhongpengcheng.blog.exception.UrlNotFoundException;
import com.zhongpengcheng.blog.pojo.Result;
import com.zhongpengcheng.blog.util.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.slf4j.MDC;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import static com.zhongpengcheng.blog.auth.filter.TraceIdFilter.TRACE_ID;
import static com.zhongpengcheng.blog.enums.ResultCodeEnum.*;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;

/**
 * 全局异常拦截，仅拦截标有{@link RestController}注解的类，其他异常在
 * com.zhongpengcheng.zblog.controller.ServletErrorController中进行拦截
 * @author zhongpengcheng
 */
@RestControllerAdvice(annotations = RestController.class)
@Slf4j
public class GlobalExceptionAdvice extends ResponseEntityExceptionHandler {

    /**
     * 账号或密码错误
     */
    @ExceptionHandler
    public Result<String> incorrectCredentialsExceptionHandler(@NonNull IncorrectCredentialsException e) {
        log.error(e.getMessage());
        return ResultUtils.failed(USER_PASSWORD_ERROR);
    }

    /**
     * 无效的访问凭证（访问token）
     */
    @ExceptionHandler
    @ResponseStatus(FORBIDDEN)
    public Result<String> credentialsExceptionExceptionHandler(@NonNull CredentialsException e) {
        log.error(e.getMessage());
        return ResultUtils.failed(INVALID_ACCESS_CREDENTIALS);
    }

    /**
     * 用户账户不存在
     */
    @ExceptionHandler
    public Result<String> unknownAccountExceptionHandler(@NonNull UnknownAccountException e) {
        log.error(e.getMessage());
        return ResultUtils.failed(USER_ACCOUNT_DOES_NOT_EXIST);
    }

    /**
     * http status code异常处理
     */
    @ExceptionHandler
    @ResponseStatus(NOT_FOUND)
    public Result<String> httpRequestException(@NonNull UrlNotFoundException e) {
        return ResultUtils.failed(e.getResultCode(), e.getMessage());
    }

    /**
     * 验证码异常处理
     */
    @ExceptionHandler
    public Result<String> captchaException(@NonNull CaptchaException e) {
        return ResultUtils.failed(e.getResultCode());
    }

    /**
     * 兜底的异常拦截
     */
    @ExceptionHandler
    public Result<String> globalExceptionHandler(@NonNull Exception e) {
        if (e instanceof BaseResultCodeException) {
            BaseResultCodeException codeException = (BaseResultCodeException) e;
            return ResultUtils.failed(codeException.getResultCode());
        }
        log.error("", e);
        return ResultUtils.failed("未知系统错误，请联系管理员，traceId=" + MDC.get(TRACE_ID));
    }

    @Override
    @NonNull
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body
            , @NonNull HttpHeaders headers, @NonNull HttpStatus status, @NonNull WebRequest request) {
        body = ResultUtils.failed(ex.getMessage());
        return super.handleExceptionInternal(ex, body, headers, status, request);
    }
}
